수치해석 실습 101#

강좌: 수치해석

낙하산병 문제#

낙하산을 맨 병사는 중력과 공기에 의한 항력을 받는다.

parachute-fig

Fig. 1 낙하산병 (From pxhere.com)#

이 때 사람에 작용하는 힘은 다음과 같다.

\[ F = F_D + F_U \]

여기서 \(F_D\) 중력에 의해 아래 방향으로 작용하는 힘이고 \(F_U\) 는 공기 저항에 의해 위로 작용하는 힘이다. 각각은 다음과 같이 표현할 수 있다.

\[\begin{split} \begin{align} F_D &= mg, \\ F_U &= -c v. \end{align} \end{split}\]

여기서 \(m\) 은 질량, \(g\) 는 중력 가속도이다. \(c\)는 항력 계수이고, \(v\) 는 속도이다.

뉴턴의 제2법칙을 적용하면 지배방정식 (Governing Equation)은 다음과 같다.

\[\begin{split} \begin{align} F &= ma = m \frac{dv}{dt} \\ \frac{dv}{dt} &= \frac{mg - cv}{m} = g - \frac{c}{m} v \end{align} \end{split}\]

엄밀해 결과 가시화#

낙하산병이 처음에 정지하고 있었다면 (\(t=0\) 일 때 \(v=0\)), 이 미분방정식의 엄밀해 (Exact Solution) 은 다음과 같다.

\[ v(t) = \frac{gm}{c} (1 - e^{-(c/m)t}) \]

낙하산 병의 몸무게 \(m=68.1 kg\), 항력계수 \(c=12.5 kg/s\)로 생각하자. 중력 가속도는 \(9.81 kg/s\)으로 가정하자.

파이썬에서는 matplotlib 패키지로 그래프를 그린다. 아래와 같이 불러온다.

%matplotlib inline
from matplotlib import pyplot as plt

plt.style.use('ggplot')
plt.rcParams['figure.dpi'] = 150

엄밀해를 계산하는 함수를 만들어보자.

import numpy as np

# 주요 상수
m = 68.1
c = 12.5
g = 9.81

# 엄밀해 계산 함수
def exact(t):
    return g*m/c*(1-np.exp(-(c/m)*t))
exact(10)
44.91892648723751

시간에 따른 엄밀해를 30초 까지 연속적으로 계산해보자. np.linspace 를 이용하면 등간격으로 시간을 구할 수 있다.

# 0~30 까지를 100 등분 함
t = np.linspace(0, 30, 101)

For Loop를 이용하면 연속적인 계산이 가능하다.

v = []

for ti in t:
    v.append(exact(ti))

Pythoniac 코딩으로 List Comprehension을 사용하면 좀 더 빠르게 계산할 수 있다.

v = [exact(ti) for ti in t]

numpy 는 벡터 연산이 가능하므로 다음과 같이 빠르게 계산할 수 있다.

v = exact(t)

plt.plot 을 이용해서 그래프를 그리자

plt.plot(t, v)
[<matplotlib.lines.Line2D at 0x7f701e117610>]
../_images/79260a0029304083c8651de3aadf91ebbbdcdb75a3caea55c1e6cc18d3280cfe.png

Note

엔지니어는 그래프의 축과 선의 의미를 꼭 표기해야 한다. (무조건 오답 처리)

plt.plot(t, v)

# x,y 축 이름
plt.xlabel('Time (s)')
plt.ylabel('Velocity (m/s)')

# 그래프 선 이름
plt.legend(['Exact Solution'])
<matplotlib.legend.Legend at 0x7f701e193490>
../_images/28644c504a86f533b3792f5a4e1acb649c5638ad50b0cf8cb2432c8dc4214be0.png

수치해석#

이를 수치적으로 풀어보자. scipy 패키지에는 다양한 과학함수와 수치해석 기법을 제공한다.

scipy.integrate 에 있는 solve_ivp 함수로 계산해보자.

# solve_ivp 함수 호출
from scipy.integrate import solve_ivp

#solve_ivp?
# 우변 함수 작성
def f(t, v):
    return g - c/m*v
sol = solve_ivp(f, [0, 30], [0])
plt.plot(t, v)

# 수치해 결과 그리기 (Marker만 그리기)
plt.plot(sol.t, sol.y[0], linestyle='None', marker='x')

# x,y 축 이름
plt.xlabel('Time (s)')
plt.ylabel('Velocity (m/s)')

# 그래프 선 이름
plt.legend(['Exact Solution', 'Numerical Solution'])

# 그래프 이름
plt.title('Velocity of Paratrooper')

# 그래프 저장
plt.savefig('velocity.png', dpi=200)
../_images/cf2c8895a29075e481bebdabac7a0d76a5ce16051cd4ea0fe0e92c749096cf9c.png

Note

수치해석은 모든 답을 줄까? No !!! (No free lunch!!!)

계산 결과를 저장하자.

ASCII 방식과 Binary 방식이 있다.

  • ASCII data: 사람이 읽을 수 있도록 숫자, 문자, 기호로 표기

  • Binary data: 컴퓨터가 저장하는 2진수 방식으로 기록

행렬 (Array)를 어떻게 구분할까?

# 데이터: (시간, 값) 으로 기록
arr = np.array([sol.t, sol.y[0]]).T
arr
array([[0.00000000e+00, 0.00000000e+00],
       [1.00000000e-04, 9.80990997e-04],
       [1.10000000e-03, 1.07899107e-02],
       [1.11000000e-02, 1.08780146e-01],
       [1.11100000e-01, 1.07885319e+00],
       [1.11110000e+00, 9.86025483e+00],
       [4.72197940e+00, 3.09793935e+01],
       [9.26404220e+00, 4.36818913e+01],
       [1.47507404e+01, 4.98740638e+01],
       [2.13260752e+01, 5.23714367e+01],
       [2.93351884e+01, 5.31924207e+01],
       [3.00000000e+01, 5.32214224e+01]])

numpysavetxt, loadtxt 함수를 활용하자.

# sol.csv 파일로 기록
np.savetxt('sol.csv', arr, delimiter=',', header='t,v', comments='')
# 확인 (Excel 에서도 읽기 가능)
with open('sol.csv') as f:
    print(f.read())
t,v
0.000000000000000000e+00,0.000000000000000000e+00
9.999999999999999124e-05,9.809909967511212543e-04
1.100000000000000066e-03,1.078991067353642086e-02
1.110000000000000049e-02,1.087801455912157239e-01
1.111000000000000043e-01,1.078853190808823914e+00
1.111099999999999977e+00,9.860254828888242784e+00
4.721979401067658344e+00,3.097939353875368340e+01
9.264042202257618541e+00,4.368189129769811530e+01
1.475074044342309776e+01,4.987406378138719276e+01
2.132607522137336886e+01,5.237143665039166507e+01
2.933518837367320486e+01,5.319242067027523291e+01
3.000000000000000000e+01,5.322142241925892847e+01